using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using CustomORM.DataAccess;

namespace CustomORM.Mapping
{
    public static class MagicMapper
    {
        static Dictionary<Type, IEntityToTableMapper> _registeredMappers = new Dictionary<Type, IEntityToTableMapper>();
        private static int _identifierCounter = 0;

        static MagicMapper()
        {
            var thisAssembly = Assembly.GetAssembly(typeof (MagicMapper));
            ScanForMappers(thisAssembly);
        }

        public static void ScanForMappers(Assembly assemblyToScan)
        {
            var mappers = assemblyToScan.GetTypes()
                // All mapper classes implement "abstract class EntityToTableMapper<T>:IEntityToTableMapper", thus are not abstract classes
                .Where(x => typeof(IEntityToTableMapper).IsAssignableFrom(x) && !x.IsAbstract && x.IsClass && !x.IsGenericType).ToList();

            foreach (var mapper in mappers)
            {
                Activator.CreateInstance(mapper);
            }
        }

        public static string AddMapper(Type type, IEntityToTableMapper mapper)
        {
            ++_identifierCounter;
            if (_registeredMappers.ContainsKey(type))
            {
                throw new Exception(string.Format("Cannot add another mapper for: {0}",type.ToString()));
            }
            _registeredMappers.Add(type, mapper);
            // we want string identifier representation, as for now it is couter
            return _identifierCounter.ToString();
        }

        public static void RemoveMapper(Type type)
        {
            _registeredMappers.Remove(type);
        }

        public static void RemoveAllMappers()
        {
            _registeredMappers.Clear();
        }

        public static IEntityToTableMapper GetMapper(object model)
        {
            var type = model.GetType();

            if (!_registeredMappers.ContainsKey(type))
            {
                throw new Exception(string.Format("Ensure that mapping exists for type {0}.", type));
            }

            return _registeredMappers[type];
        }

        public static IEntityToTableMapper GetMapper(Type type)
        {
            if (!_registeredMappers.ContainsKey(type))
            {
                throw new Exception(string.Format("Ensure that mapping exists for type {0}.", type));
            }

            return _registeredMappers[type];
        }

        public static RecordData GetRecordData(object model)
        {
            var mapper = GetMapper(model);

            return mapper.GetRecordData(model);
        }
    }
}